package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.common.JsonResult;
import cn.tedu.charging.common.pojo.param.ChargingProcessParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.common.pojo.vo.GunInfoVO;
import cn.tedu.charging.cost.dao.repository.ChargingDataESRepository;
import cn.tedu.charging.cost.dao.repository.CostCacheRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleRepository;
import cn.tedu.charging.cost.feign.DeviceClient;
import cn.tedu.charging.cost.pojo.po.ChargingDataPO;
import cn.tedu.charging.cost.pojo.po.CostRulePO;
import cn.tedu.charging.cost.service.ChargingCostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.sql.Connection;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * @Author: Miyako
 * @Date: 2024-10-22-15:29
 * @Description:
 */
@Slf4j
@Service
public class ChargingCostServiceImpl implements ChargingCostService {

    @Autowired
    private DeviceClient deviceClient;
    @Autowired
    private ChargingDataESRepository chargingDataESRepository;

    @Override
    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
        ChargingDataPO chargingDataPO = getChargingDataByEs(chargingProcessParam);
        log.debug("通过订单号:{},获取本次充电数据:{}度,当前同步次数:{}",
                chargingProcessParam.getOrderNo(), chargingDataPO, chargingDataPO.getCount());
        Integer stationId = chargingProcessParam.getStationId();
        Integer gunId = chargingProcessParam.getGunId();
        Integer currentHour = getCurrentHour();
        log.debug("获取到当前时间{}",currentHour);
        List<CostRulePO> costRulePOList = getCostRules(stationId,gunId);
        log.debug("通过站点id：{}获取到计费列表{}",stationId,costRulePOList);
        CostRulePO costRulePO = getCostByTimeAndGun(costRulePOList,currentHour);
        if (costRulePO != null){
            String name = costRulePO.getName();
            //当前时间电费
            BigDecimal powerFee = costRulePO.getPowerFee();
            if (chargingDataPO.isFirst()){
                //判断是否第一次计算电费，是则直接 存到ES
                firstCost2ChargingData(chargingProcessParam,chargingDataPO,name,powerFee,currentHour);
            }else {
                //不是第一次则计算查到总价后计算出总价 存入ES
                lastCost2ChargingData(chargingProcessParam,chargingDataPO,name,powerFee,currentHour);
            }
        }
        else {
            //记录日志,方便快速定位问题 告警
            log.error("场站id:{},当前时间:{},获取计价规则失败,告警告警告警,接入告警系统",stationId,currentHour);
        }
        ChargingProcessVO chargingProcessVO = new ChargingProcessVO();
        BeanUtils.copyProperties(chargingDataPO , chargingProcessVO);
        return chargingProcessVO;
    }


    private void firstCost2ChargingData(ChargingProcessParam param, ChargingDataPO chargingDataPO, String name, BigDecimal powerFee, Integer currentHour) {
        log.debug("第一次同步充电数据");
        //获取充电度数
        Float chargingCapacity = param.getChargingCapacity();
        //计算价格 充电度数 * 每度电的价格
        BigDecimal currentCost = getCost( powerFee,chargingCapacity);
        log.debug("当前时间(小时):{}," +
                "计价规则名称:{}," +
                "充电度数:{}," +
                "电费单价:{}," +
                "本次花费:{}," +
                "总花费:{}",currentHour,name,chargingCapacity,powerFee,currentCost,currentCost);
        //保存当前的话费到充电数据,为了下次 同步数据的时候 做  <<<价格的累加>>> 准备
        chargingDataPO.setTotalCost(currentCost);
        //保存当前充电的度数到充电数据,设备同步的度数是<<<包含关系>>>
        // 为了下次 <<<计算真实充电度数>>> 做准备
        chargingDataPO.setChargingCapacity(chargingCapacity);
        //每次改变ChargingData,都需要把最新的充电数据保存到ES
        //save 不存在 保存,存在就通过id更新
        chargingDataESRepository.save(chargingDataPO);
    }

    private void lastCost2ChargingData(ChargingProcessParam chargingProcessParam, ChargingDataPO chargingDataPO, String name, BigDecimal powerFee, Integer currentHour) {
        log.debug("第{}次同步充电数据",chargingDataPO.getCount());
        //获取到本次充电数
        Float capacity = chargingDataPO.getChargingCapacity(chargingProcessParam.getChargingCapacity());
        //获取到本次价格
        BigDecimal currentCost = getCost(powerFee, capacity);
        chargingDataPO.setTotalCost(currentCost);
        log.debug("当前时间(小时):{}," +
                        "计价规则名称:{}," +
                        "充电度数:{}," +
                        "电费单价:{}," +
                        "本次花费:{}," +
                        "总花费:{}",currentHour,name,capacity,
                powerFee,currentCost,chargingDataPO.getTotalCost());
        chargingDataPO.setChargingCapacity(chargingProcessParam.getChargingCapacity());
        chargingDataESRepository.save(chargingDataPO);
    }


    private ChargingDataPO getChargingDataByEs(ChargingProcessParam  processParam){
        String orderNo = processParam.getOrderNo();
        Optional<ChargingDataPO> optional = chargingDataESRepository.findById(orderNo);
        //看数据是否第一次同步
        if (optional.isPresent()){
            //有数据,第N次同步
            ChargingDataPO chargingDataPO = optional.get();
            Float capacity = chargingDataPO.getChargingCapacity(processParam.getChargingCapacity());
            chargingDataPO.setUserId(processParam.getUserId());
            chargingDataPO.setCount(chargingDataPO.getCount()+1);
            log.debug("总耗电:{},本次耗电{}",processParam.getChargingCapacity(),capacity);
            return chargingDataPO;
        }else {
            log.debug("第一次同步数据");
            ChargingDataPO chargingDataPO = initESChargingData(processParam);
            chargingDataESRepository.save(chargingDataPO);
            return chargingDataPO;
        }

    }

    private ChargingDataPO initESChargingData( ChargingProcessParam processParam) {
        ChargingDataPO chargingDataPO = new ChargingDataPO();
        Float capacity = processParam.getChargingCapacity();
        String orderNo = processParam.getOrderNo();
        log.debug("初始化订单{}同步数据中----",orderNo);
        chargingDataPO.setUserId(processParam.getUserId());
       chargingDataPO.setChargingCapacity(capacity);
       chargingDataPO.setCount(1);
       chargingDataPO.setId(orderNo);
       chargingDataPO.setOrderNo(orderNo);
       chargingDataPO.setGunId(processParam.getGunId());
       chargingDataPO.setStartTime(System.currentTimeMillis());
        return chargingDataPO;
    }

    //计算话费
    private BigDecimal getCost(BigDecimal powerFee, Float chargingCapacity) {

        BigDecimal bigDecimal = BigDecimal.valueOf(chargingCapacity);
        //multiply是相乘的方法
        return powerFee.multiply(bigDecimal);

    }

    //通过时间和枪类型去计费列表中找到
    private CostRulePO getCostByTimeAndGun(List<CostRulePO> costRulePOList, Integer currentHour) {
        for (CostRulePO rule:costRulePOList){
            Integer startTime = rule.getStartTime();
            Integer endTime = rule.getEndTime();
            if (startTime <= currentHour &&  currentHour < endTime){
                return rule;
            }
        }
        return null;
    }

    private Integer getCurrentHour() {
        LocalDateTime now = LocalDateTime.now();
        return now.getHour();
    }
    @Autowired
    CostRuleRepository costRuleRepository;

    @Autowired
    CostCacheRepository costCacheRepository;
    //获取当前站点的计费规则列表
    private List<CostRulePO> getCostRules(Integer stationId, Integer gunId) {
        //查询到枪类型
        JsonResult<GunInfoVO> gunInfo = deviceClient.getGunInfo(gunId);
        List<CostRulePO> costRules = null;
        Integer gunType = gunInfo.getData().getGun_type();
        costRules = costCacheRepository.getCostRuleByStationId(stationId);
        //先去redis中查询这个规则表是否存在
         if (!CollectionUtils.isEmpty(costRules)){
             log.debug("通过场站id:{},从redis中查询到{},redis有数据直接返回",stationId,costRules);
            return costRules;
         }
         else{
             log.debug("redis没有数据,通过数据库查询");
             //再去数据库中获取,并存入redis中（作为热数据使用 性能更好）
              costRules = costRuleRepository.getCostRules(stationId, gunType);
              if (!CollectionUtils.isEmpty(costRules)){
                  long startTime = System.currentTimeMillis();
                  log.debug("通过哦场站id{},从数据库中查询到数据{},保存到Redis中",stationId,costRules);
                  costCacheRepository.saveCostRules(stationId,costRules);
                  long EndTime = System.currentTimeMillis();
                  log.debug("保存成功,耗时{}",startTime-EndTime);
                  return costRules;
              }else {
                  log.debug("没有找到场站{}相关的计价规则",stationId);
                  //返回空列表，避免空指针
                  return Collections.emptyList();
              }

         }

    }

}
